#ifndef Task_H
#define Task_H
#include <functional>
#include "ccoroutine_core_global.h"
#include "coroutine.h"


namespace CCoroutine{
template<typename T,typename ...Args>
class Task;

template<typename TaskType>
class TaskAwaiter;
template<typename T,typename ...Args>
class TaskController;
}

template<typename T,typename ...Args>
CCoroutine::AwaiterBase* getAwaiter(CCoroutine::TaskController<T,Args...>& con);
template<typename T,typename ...Args>
CCoroutine::AwaiterBase* getAwaiter(CCoroutine::TaskController<T,Args...>&& con);
template<typename T,typename ...Args>
CCoroutine::AwaiterBase* getAwaiter(CCoroutine::Task<T,Args...>* task);

namespace CCoroutine{
//用于作为异步函数的协程
template<typename T,typename ...Args>
class Task:public ArgedPromise<Args...>
{
public:
    typedef TaskController<T,Args...> Controller;
    Task(){

    }

    void on_return_value(T v){
        m_value = v;
    }

    T return_value(){
        return m_value;
    }

    class FinalSuspend:public AwaiterBase{
    public:
        FinalSuspend(const std::vector<PromiseBase*>& awaits):awaits(awaits){

        }

        virtual bool ready(){
            return false;
        }
        virtual void suspend(PromiseBase* p){
            Task* t = dynamic_cast<Task*>(p);
            for(auto i:awaits)
            {
                i->resume();
            }

            t->ref--;
            if(!t->ref)
            {
                delete p;
            }
        }
        virtual void resume(){

        }

    private:
        std::vector<PromiseBase*> awaits;
    };

    AwaiterBase* initial_suspend(){
        return new SuspendNever();
    }

    AwaiterBase* final_suspend(){
        finished = true;
        return new FinalSuspend(awaits);
    }

    template<typename R>
    class ThenTask:public Task<R,std::function<R(T t)>,Task*>
    {
        coro_begin;
        coro_await(std::get<1>(this->args))
        result = std::get<0>(this->args)(std::get<1>(this->args)->return_value());
        coro_return_value(result)
        coro_end;

        R result;
        std::function<R(T t)> func;
    };

    template<typename R>
    typename Task::ThenTask<R>::Controller then(const std::function<R(T t)>& func){
        return createCoro<Task::ThenTask<R>>(func,this);
    }

private:
    T m_value;
    int ref = 2;
    std::vector<PromiseBase*> awaits;
    bool finished = false;
    template<typename T2,typename ...Args2>
    friend class TaskController;

    template<typename TaskType>
    friend class TaskAwaiter;
};

template<typename T,typename ...Args>
class TaskController:public ControllerBase{
    typedef Task<T,Args...> Task;
public:
    TaskController(){

    }
    TaskController(TaskController&& o):ControllerBase(std::move(o)){

    }

    TaskController& operator =(TaskController&& o){
        ControllerBase::operator =(std::move(o));
        return *this;
    }

    Task* getTask(){
        return dynamic_cast<Task*>(base);
    }

    T result(){
        auto t = dynamic_cast<Task*>(base);
        if(!t) return {};
        return t->return_value();
    }

    ~TaskController(){
        auto t = dynamic_cast<Task*>(base);
        if(!t) return;
        t->ref--;
        if(!t->ref)
        {
            delete base;
        }
    }

};


template<typename TaskType>
class TaskAwaiter:public AwaiterBase
{
public:
    TaskAwaiter(TaskType* p):p(p){

    }
    bool ready(){return p->finished;}
    void suspend(PromiseBase* p){
        this->p->awaits.push_back(p);
    };
    void resume(){
    }

private:
    TaskType* p;
};


}

template<typename T,typename ...Args>
CCoroutine::AwaiterBase* getAwaiter(CCoroutine::TaskController<T,Args...>& con){
    return new CCoroutine::TaskAwaiter<CCoroutine::Task<T,Args...>>(con.getTask());
}

template<typename T,typename ...Args>
CCoroutine::AwaiterBase* getAwaiter(CCoroutine::TaskController<T,Args...>&& con){
    return new CCoroutine::TaskAwaiter<CCoroutine::Task<T,Args...>>(con.getTask());
}

template<typename T,typename ...Args>
CCoroutine::AwaiterBase* getAwaiter(CCoroutine::Task<T,Args...>* task){
    return new CCoroutine::TaskAwaiter<CCoroutine::Task<T,Args...>>(task);
}

#endif
